from enum import IntEnum
from typing import List

from core.bytecodes import STOP


class ObjectType(IntEnum):
    NUMBER = 0
    CHARACTERS = 1
    BOOLEAN = 2
    SYMBOLS = 3
    PROCESS = 4


class Value:
    def __init__(self, obj_type: ObjectType, val):
        self.obj_type = obj_type
        self.val = val

    def __eq__(self, other):
        return self.obj_type == other.obj_type and self.val == other.val

    def __hash__(self):
        return hash(self.val)


def AS_NUM(value) -> Value:
    return Value(ObjectType.NUMBER, value)


def IS_NUM(value) -> bool:
    return value.obj_type == ObjectType.NUMBER


def AS_STR(value) -> Value:
    return Value(ObjectType.CHARACTERS, value)


def IS_STR(value) -> bool:
    return value.obj_type == ObjectType.CHARACTERS


def AS_BOOL(value) -> Value:
    return Value(ObjectType.BOOLEAN, value)


def IS_BOOL(value) -> bool:
    return value.obj_type == ObjectType.BOOLEAN


def AS_SYM(value) -> Value:
    return Value(ObjectType.SYMBOLS, value)


def IS_SYM(value) -> bool:
    return value.obj_type == ObjectType.SYMBOLS

def NUM_VAL(value)-> int:
    return value.val


class Function(Value):
    def __init__(self, name: Value, args: list[Value], body_codes: []):
        self.obj_type = ObjectType.PROCESS
        self.name = name
        self.args = args
        self.body = body_codes
        self.arity = len(args)


class ExecutorObject:

    def __init__(self):
        self.codes = []
        self.constants: List[Value] = []
        self.functions : dict[Value, Function] = {}
        self.globals = {}

    def add_constant(self, value):
        self.constants.append(value)
        return len(self.constants) - 1

class CallFrame:
    def __init__(self):
        self.fn : Function = None
        self.ip = 0
        self.base_pointer = 0
        self.locals = {}

    def init(self, fn, base_pointer):
        self.fn = fn
        self.base_pointer = base_pointer

    def free(self):
        self.fn = None
        self.ip = 0
        self.base_pointer = 0

    def get_codes(self) -> list[int]:
        return self.fn.body

    def read_byte(self):
        if self.ip >= len(self.get_codes()):
            return STOP
        else:
            self.ip += 1
            return self.get_codes()[self.ip - 1]

    def add_local(self, var_name, val):
        self.locals[var_name] = val